﻿using System.Data;
using System.Data.Common;
using System.Linq;
using System.Collections.Generic;
using RMS.Utils.EventAggregators;
using RMS.Utils.RulesManager;
using Microsoft.Practices.ServiceLocation;

namespace RMS.DataAccess.Common
{

    public abstract class RepositoryBase<TEntity, TIdentity, TEnum> : ReadOnlyRepositoryBase<TEntity, TEnum>
        where TEntity : IEntity, new()
        where TIdentity : struct
        where TEnum : struct
    {
        protected IEventAggregator EventAggregator { get; private set; }
        #region [ Constructor ]
        protected RepositoryBase(string connectionStringName)
            : base(connectionStringName)
        {
            EventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
        }
        #endregion

        #region [ abstract fields]
        /// <summary>
        ///  Insert Stored Procedure Name
        /// </summary>
        protected abstract string InsertProc { get; }
        /// <summary>
        ///  Update Stored Procedure Name
        /// </summary>
        protected abstract string UpdateProc { get; }
        /// <summary>
        /// Delete Stored Procedure Name
        /// </summary>
        protected abstract string DeleteProc { get; }
        #endregion

        #region [ Get Parameters ]
        /// <summary>
        ///  Create Db Parameters
        /// </summary>
        /// <param name="name">Parameter name</param>
        /// <param name="value">Parameter value</param>
        /// <returns></returns>
        protected DbParameter CreateParameter(string name, object value)
        {
            DbParameter newParameter = _connectionType.CreateParameter();
            newParameter.ParameterName = name;
            newParameter.Value = value;
            return newParameter;
        }
        protected DbParameter CreateParameter(string name, object value, ParameterDirection direction)
        {
            DbParameter newParameter = _connectionType.CreateParameter();
            newParameter.ParameterName = name;
            newParameter.Value = value;
            newParameter.Direction = direction;
            return newParameter;
        }
        #endregion



        /// <summary>
        /// Insert Entity
        /// </summary>
        /// <param name="entity">Entity</param>
        public TIdentity Insert(TEntity entity)
        {
            return Insert(entity, null);
        }
        public TIdentity Insert(TEntity entity, DbTransaction dbTransaction)
        {
            TIdentity Id;
            this.EventAggregator.GetEvent<EntityAction>().Publish(new EntityActionEventArgs<TEntity>(EntityState.Inserting, entity));
            using (var connection = dbTransaction != null ? dbTransaction.Connection : CreateConnection())
            {
                // var connection = dbTransaction != null ? dbTransaction.Connection : CreateConnection();
                DbCommand command = connection.CreateCommand();
                command.Transaction = dbTransaction;
                command.Parameters.AddRange(GetInsertParameters(entity).ToArray());
                command.CommandText = InsertProc;
                Execute(command, dbTransaction == null);
                Id = (TIdentity)command.Parameters["Identity"].Value;
            }
            this.EventAggregator.GetEvent<EntityAction>().Publish(new EntityActionEventArgs<TEntity>(EntityState.Inserted, entity));
            return Id;
        }
        /// <summary>
        /// Update Entity
        /// </summary>
        /// <param name="entity">Entity</param>
        public void Update(TEntity entity)
        {
            Update(entity, null);
        }
        public void Update(TEntity entity, DbTransaction dbTransaction)
        {
            this.EventAggregator.GetEvent<EntityAction>().Publish(new EntityActionEventArgs<TEntity>(EntityState.Updating, entity));
            using (var connection = dbTransaction != null ? dbTransaction.Connection : CreateConnection())
            {
                //var connection = dbTransaction != null ? dbTransaction.Connection : CreateConnection();
                DbCommand command = connection.CreateCommand();
                command.Transaction = dbTransaction;
                command.Parameters.AddRange(GetUpdateParameters(entity).ToArray());
                command.CommandText = UpdateProc;
                Execute(command, dbTransaction == null);
            }
            this.EventAggregator.GetEvent<EntityAction>().Publish(new EntityActionEventArgs<TEntity>(EntityState.Updated, entity));
        }
        /// <summary>
        /// Delete Entity
        /// </summary>
        /// <param name="id">Entity ID</param>
        public void Delete(TIdentity id)
        {
            Delete(id, null);
        }
        public void Delete(TIdentity id, DbTransaction dbTransaction)
        {
            this.EventAggregator.GetEvent<EntityAction>().Publish(new EntityActionEventArgs<TIdentity>(EntityState.Deleting, id));
            using (var connection = dbTransaction != null ? dbTransaction.Connection : CreateConnection())
            {
                // var connection = dbTransaction != null ? dbTransaction.Connection : CreateConnection();
                DbCommand command = connection.CreateCommand();
                command.Transaction = dbTransaction;
                command.Parameters.AddRange(GetDeleteParameters(id).ToArray());
                command.CommandText = DeleteProc;
                Execute(command, dbTransaction == null);
            }
            this.EventAggregator.GetEvent<EntityAction>().Publish(new EntityActionEventArgs<TIdentity>(EntityState.Deleted, id));
        }
        /// <summary>
        ///  Insert more than one Entity
        /// </summary>
        /// <param name="entities"></param>
        public void BulckInsert(TEntity[] entities)
        {
            using (var connection = CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    foreach (TEntity entity in entities)
                    {
                        Insert(entity, transaction);
                    }
                    transaction.Commit();
                }

                catch
                {
                    transaction.Rollback();
                }
                finally
                {
                    connection.Close();
                }
            }
        }
        /// <summary>
        ///  Update more than one Entity
        /// </summary>
        /// <param name="entities"></param>
        public void BulckUpdate(TEntity[] entities)
        {
            using (var connection = CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    foreach (TEntity entity in entities)
                    {
                        Update(entity, transaction);
                    }

                    transaction.Commit();
                }

                catch
                {
                    transaction.Rollback();
                }
                finally
                {
                    connection.Close();
                }
            }
        }
        /// <summary>
        /// Delete more than one Entity
        /// </summary>
        /// <param name="ids"></param>
        public void BulckDelete(TIdentity[] ids)
        {
            using (var connection = CreateConnection())
            {
                connection.Open();
                DbTransaction transaction = connection.BeginTransaction();
                try
                {
                    foreach (TIdentity id in ids)
                    {
                        Delete(id, transaction);
                    }
                    transaction.Commit();
                }
                catch
                {
                    transaction.Rollback();
                }
                finally
                {
                    connection.Close();
                }
            }
        }
        /// <summary>
        /// Get Insert Strord procedure Parameters
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        protected abstract IEnumerable<DbParameter> GetInsertParameters(TEntity entity);
        /// <summary>
        /// Get Update Strord procedure Parameters
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        protected abstract IEnumerable<DbParameter> GetUpdateParameters(TEntity entity);
        /// <summary>
        ///  Get Delete Strord procedure Parameters
        /// </summary>
        /// <param name="identity"></param>
        /// <returns></returns>
        protected abstract IEnumerable<DbParameter> GetDeleteParameters(TIdentity identity);
    }
}